package chapter7;

import java.util.*;

/**
 * @author yuh
 * @date 2018/5/8 19:34
 * @description 敏感词过滤
 */
public class SensitiveWordFilter {

    /**
     * 实例
     */
    public static SensitiveWordFilter INSTANCE = null;

    /**
     * trie树
     */
    private Map<String, Object> sensitiveMap = null;

    /**
     * 结束标记
     */
    private static final String END_FLAG = "isEnd";

    public void init() {
        INSTANCE = this;
        //进行 初始化词组
        List<String> hashSet = new ArrayList<>();
        hashSet.add("自杀");
        hashSet.add("三级片");
        hashSet.add("法轮");
        init(hashSet);
    }

    /**
     * 获得所有非法词语
     *
     * @param txt 原始字符串
     * @return
     */
    public List<String> getAllSensitiveWords(String txt) {
        List<String> words = new ArrayList<>();
        for (int i = 0; i < txt.length(); i++) {
            int matchSentiveWordPos = getMatchSentiveWordPos(txt, i, 0);
            if (matchSentiveWordPos != 0) {
                words.add(txt.substring(i, i + matchSentiveWordPos));
                i += matchSentiveWordPos;
            }
        }
        return words;
    }

    /**
     * 替换所有的敏感词语为指定词语
     *
     * @param txt         原始字符串
     * @param legalString 合法词语
     * @return
     */
    public String replaceSensitiveWord(String txt, String legalString) {
        List<String> allSensitiveWords = getAllSensitiveWords(txt);
        for (String sensitiveWord : allSensitiveWords) {
            txt = txt.replaceAll(sensitiveWord, legalString);
        }
        return txt;
    }

    /**
     * 添加新的敏感词
     *
     * @param sensitiveWords
     */
    public synchronized void addSensitiveWords(String... sensitiveWords) {
        Map<String, Object> nowMap = null;
        Map<String, Object> tmpMap = null;
        for (String sensitiveWord : sensitiveWords) {
            nowMap = sensitiveMap;
            for (int i = 0; i < sensitiveWord.length(); i++) {
                char c = sensitiveWord.charAt(i);
                Object o = nowMap.get(String.valueOf(c));
                if (o != null) {
                    nowMap = (Map<String, Object>) o;
                } else {
                    tmpMap = new HashMap<>();
                    tmpMap.put(END_FLAG, "0");
                    nowMap.put(String.valueOf(c), tmpMap);
                    nowMap = tmpMap;
                }
                if (i == sensitiveWord.length() - 1) {
                    nowMap.put(END_FLAG, "1");
                }
            }
        }
    }

    /**
     * 读取敏感词库，将敏感词放入HashSet中，构建一个DFA算法模型：
     * 中 = {
     * isEnd = 0
     * 国 = {
     * isEnd = 1
     * 人 = {isEnd = 0
     * 民 = {isEnd = 1}
     * }
     * 男  = {
     * isEnd = 0
     * 人 = {
     * isEnd = 1
     * }
     * }
     * }
     * }
     * 五 = {
     * isEnd = 0
     * 星 = {
     * isEnd = 0
     * 红 = {
     * isEnd = 0
     * 旗 = {
     * isEnd = 1
     * }
     * }
     * }
     * }
     *
     * @param sensitiveWords 敏感词库
     */
    private void init(List<String> sensitiveWords) {
        sensitiveMap = new HashMap<>(sensitiveWords.size());
        addSensitiveWords(sensitiveWords.toArray(new String[sensitiveWords.size()]));
    }


    /**
     * 从开头获取一个敏感词
     *
     * @param txt        原始字符串
     * @param beginIndex 起始索引
     * @param matchType  0.最大匹配 1.最小匹配
     * @return
     */
    private int getMatchSentiveWordPos(String txt, int beginIndex, int matchType) {
        int matchPos = 0;
        boolean matchFlag = false;
        Map<String, Object> nowMap = sensitiveMap;
        for (int i = beginIndex; i < txt.length(); i++) {
            char c = txt.charAt(i);
            Object o = nowMap.get(String.valueOf(c));
            if (o != null) {
                matchPos += 1;
                nowMap = (Map<String, Object>) o;
                if ("1".equals(nowMap.get(END_FLAG))) {
                    matchFlag = true;
                    if (matchType == 1) {
                        //最小
                        break;
                    }
                }
            } else {
                break;
            }
        }
        if (!matchFlag) {
            matchPos = 0;
        }
        return matchPos;
    }

}
